package com.cx.utils;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import io.lettuce.core.protocol.LettuceCharsets;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.CollectionUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
/**
 * ProtoStuffUtil
 *
 * @author 舒建辉
 * @date 2019-1-8
 */
public class ProtoStuffUtil {

    //-----------------------------------------------
    //          只序列化/反序列化对象
    //          序列化的结果里，不包含类型的信息
    //-----------------------------------------------

    /**
     * 将对象序列化为字节数组
     *
     * @param object 任意对象
     * @param <T> 对象的类型
     * @return 序列化后的字节数组
     */
    public static <T> byte[] writeObjectToByteArray(T object) {
        if (object == null) {
            throw new RuntimeException("序列化对象(" + object + ")!");
        }

        Schema<T> schema = (Schema<T>) RuntimeSchema.getSchema(object.getClass());
        LinkedBuffer buffer = LinkedBuffer.allocate(CacheConsts.DEFAULT_BUFFER_SIZE);

        try {
            return ProtostuffIOUtil.toByteArray(object, schema, buffer);
        } catch (Exception e) {
            throw new RuntimeException("序列化(" + object.getClass() + ")对象(" + object + ")发生异常!", e);
        } finally {
            buffer.clear();
        }
    }

    /**
     * 将对象序列化为 String
     * 利用了 Base64 编码
     *
     * @param object 任意对象
     * @param <T> 对象的类型
     * @return 序列化后的字符串
     */
    public static <T> String serialize(T object) {

        return new String(writeObjectToByteArray(object), LettuceCharsets.UTF8);
    }

    /**
     * 将字节数组反序列化为原对象
     *
     * @param byteArray writeToByteArray 方法序列化后的字节数组
     * @param clazz     原对象的 Class
     * @param <T>       原对象的类型
     * @return 原对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T readObjectFromByteArray(byte[] byteArray, Class<T> clazz) {
        if (ArrayUtils.isEmpty(byteArray)) {
            throw new RuntimeException("反序列化对象发生异常,byte序列为空!");
        }

        try {
            T instance = clazz.newInstance();
            Schema<T> schema = RuntimeSchema.getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(byteArray, instance, schema);
            return instance;
        } catch (InstantiationException  e1) {
            throw new RuntimeException("反序列化过程中依据类型创建对象失败!", e1);
        } catch(IllegalAccessException e2){
            throw new RuntimeException("反序列化过程中依据类型创建对象失败!", e2);
        }
    }

    /**
     * 将 String 反序列化为原对象
     * 利用了 Base64 编码
     *
     * @param data   writeToString 方法序列化后的字符串
     * @param clazz 原对象的 Class
     * @param <T>   原对象的类型
     * @return 原对象
     */
    public static <T> T deserialize(String data, Class<T> clazz) {

        return readObjectFromByteArray(data.getBytes(LettuceCharsets.UTF8), clazz);
    }

    //-----------------------------------------------
    //          序列化/反序列化对象，及类型信息
    //          序列化的结果里，包含类型的信息
    //          反序列化时不再需要提供类型
    //-----------------------------------------------

    /**
     * 将对象【及类型】序列化为字节数组
     *
     * @param objList 任意对象
     * @param <T> 对象的类型
     * @return 序列化后的字节数组
     */
    public static <T> byte[] writeToByteArray(List<T> objList) {
        if (CollectionUtils.isEmpty(objList)) {
            throw new RuntimeException("序列化对象列表(" + objList + ")参数异常!");
        }

        @SuppressWarnings("unchecked")
        Schema<T> schema = (Schema<T>) RuntimeSchema.getSchema(objList.get(0).getClass());
        LinkedBuffer buffer = LinkedBuffer.allocate(CacheConsts.DEFAULT_BUFFER_SIZE);

        ByteArrayOutputStream bos = null;
        try {
            bos = new ByteArrayOutputStream();
            ProtostuffIOUtil.writeListTo(bos, objList, schema, buffer);
            return bos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException("序列化对象列表(" + objList + ")发生异常!", e);
        } finally {
            buffer.clear();
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将对象【及类型】序列化为 String
     * 利用了 Base64 编码
     *
     * @param objList 任意对象
     * @param <T> 对象的类型
     * @return 序列化后的字符串
     */
    public static <T> String serializeList(List<T> objList) {

        return new String(writeToByteArray(objList), LettuceCharsets.UTF8);
    }

    /**
     * 将字节数组反序列化为原对象
     *
     * @param byteArray writeToByteArray 方法序列化后的字节数组
     * @param <T>       原对象的类型
     * @return 原对象
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> readFromByteArray(byte[] byteArray, Class<T> clazz) {
        if (ArrayUtils.isEmpty(byteArray)) {
            throw new RuntimeException("反序列化对象发生异常,byte序列为空!");
        }

        try {
            Schema<T> schema = RuntimeSchema.getSchema(clazz);
            return ProtostuffIOUtil.parseListFrom(new ByteArrayInputStream(byteArray), schema);
        } catch (IOException e) {
            throw new RuntimeException("反序列化对象列表发生异常!", e);
        }
    }

    /**
     * 将 String 反序列化为原对象
     * 利用了 Base64 编码
     *
     * @param data writeToString 方法序列化后的字符串
     * @param <T> 原对象的类型
     * @return 原对象
     */
    public static <T> List<T> deserializeList(String data, Class<T> clazz) {
        return readFromByteArray(data.getBytes(LettuceCharsets.UTF8), clazz);
    }

}